package com.example.config.shiro;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    @Value("${shiro.global-session-time-out}")
    private Long globalSessionTimeout;

    @Bean
    public CredentialsMatcher getCredentialsMatcher() {
        return new AddSaltCredentialsMatcher();
    }

    @Bean
    public Realm realm(CredentialsMatcher credentialsMatcher) {
        CustomRealm customRealm = new CustomRealm();
        // 使用默认的密码校验规则（SimpleCredentialsMatcher）
        // 设置密码校验器
        customRealm.setCredentialsMatcher(credentialsMatcher);
        return customRealm;
    }

    @Bean
    public SessionManager sessionManager() {
        CustomSessionManager customSessionManager = new CustomSessionManager();
        // 不使用Cookie方式存储SessionId
        customSessionManager.setSessionIdCookieEnabled(false);
        // 设置全局的会话过期时间
        customSessionManager.setGlobalSessionTimeout(globalSessionTimeout);// 10分钟
        // 这里修改一些配置
        return customSessionManager;
    }

    /**
     *
     * @param realm 注入上一个Realm的对象
     * @return
     */
    @Bean
    public SecurityManager securityManager(Realm realm, SessionManager sessionManager) {
        DefaultWebSecurityManager securityManager =
                new DefaultWebSecurityManager();
        securityManager.setRealm(realm);
        // 会话管理器
        securityManager.setSessionManager(sessionManager);
        // 注入到SecurityUtils中
        SecurityUtils.setSecurityManager(securityManager);
        return securityManager;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 没有登录时，重定向的地址
        // 如果是单体项目（应该重定向到一个登录页）
        // 如果是前后端分离的项目（应该重定向到一个固定的接口，由这个接口响应一些特殊的信息）
        shiroFilterFactoryBean.setLoginUrl("/no_login"); // 自定义的接口
        // 设置过滤器链
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap());
        return shiroFilterFactoryBean;
    }

    private Map<String, String> filterChainDefinitionMap() {
        /* 必须使用LinkedHashMap保持存入的顺序 */
        Map<String, String> filters = new LinkedHashMap<>();
        // 公开访问的接口
        filters.put("/login", "anon");
        filters.put("/registry", "anon");
        filters.put("/pub/**", "anon"); // 预留一个类存放其他公开的资源
        // 需要登录才能访问的接口
        // filters.put("/loginInfo", "authc");
        filters.put("/**", "authc"); // 除开上述几个配置外，其他接口都是需要登录的
        // 需要特殊标识的权限接口（结合Controller层方法的注解）
        return filters;
    }

    /* 开启Controller方法级别的权限控制 */
    /* 登录的用户对加了注解的方法调用时，会走到CustomRealm的AuthorizationInfo方法 */
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator =
                new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =
                new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}
